-- DungeonAreaM
-- Create by zouyb
-- 管理地牢区域地图信息

module("DungeonAreaM", package.seeall);

-- 所有的区域信息
local areas = {};

-- 主区域列表
local mainAreaList = {};

-- 子区域列表
local subAreaList = {};

-- 开启的冈布奥食材信息
local openRawFoods = {};

-- 当前关闭的地牢
local closedArea = {1004, 1003};

local PASS_DUNGEON_PATH = "dungeon_area/passed_dungeon";
local OPEN_DUNGEON_PATH = "dungeon_area/opened_dungeon";
local UNLOCK_BONUS_PATH = "dungeon_area/unlock_bonus";

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    mainAreaList = {};
    subAreaList  = {};
    openRawFoods = {};
    coverList    = {};

    -- 载入区域配置表
    local dungeon_area_CSV = get_dungeon_area_CSV();
    for i = 1, #dungeon_area_CSV do
        local row = dungeon_area_CSV[i];
        local id  = row.id;

        areas[id] = dungeon_area_CSV[i];

        if row.type == "main" or row.type == "dlc" or row.type == "child" then
            if row.parent_id == 0 then
                mainAreaList[id] = dungeon_area_CSV[i];
            else
                if subAreaList[row.parent_id] == nil then
                    subAreaList[row.parent_id] = {};
                end

                table.insert(subAreaList[row.parent_id], id);
            end
        end

        -- 存放食材的来源区域
        local foods = dungeon_area_CSV[i].open_raw_food;
        if type(foods) == 'table' and #foods > 0 then
            for _, foodId in pairs(foods) do
                if type(openRawFoods[foodId]) ~= 'table' then
                    openRawFoods[foodId] = { id };
                elseif table.indexOf(openRawFoods[foodId], id) == -1 then
                    table.insert(openRawFoods[foodId], id);
                end
            end
        end
    end

    -- 子区域列表最后放上父ID
    for parentId, list in pairs(subAreaList) do
        table.insert(list, parentId);
    end

    -- 排序一遍星级层数
    for id, data in pairs(areas) do
        table.sort(data.star);
    end

    dungeon_area_CSV = {};

    -- 载入迷宫板砖配置表
    local dungeon_cover_CSV = get_dungeon_cover_CSV();

    for i = 1, #dungeon_cover_CSV do
        coverList[dungeon_cover_CSV[i].map_style] = dungeon_cover_CSV[i];
    end

    dungeon_cover_CSV = {};
end

-- 模块初始化
function init()
    loadCsv();
end

-- 检索地牢区域信息
function query(id, path)
    local m = areas[id];

    if not m or path == "" or not path then
        return m;
    end

    return m[path];
end

-- 获取地牢板砖信息
function queryCover(mapStyle, path)
    local m = coverList[mapStyle];

    if not m or not path or path == "" then
        return m;
    end

    return m[path];
end

-- 返回所有区域信息
function getAreas()
    return areas;
end

-- 返回主区域信息
function getMainAreas()
    return mainAreaList;
end

-- 返回子区域列表
function getSubAreaList(mainAreaId)
    return subAreaList[mainAreaId] or {};
end

---------------------------------
-- 取得可获得食材的区域
-- @param foodId 食材id（物品表中的编号）
function getFoodOpenAreas(foodId)
    return openRawFoods[foodId] or {};
end

------------------------------------------------
-- 设置当前地图区域
-- @function [parent=#DungeonAreaM] setCurrentArea
-- @param user 玩家对象
-- @param id   地牢区域ID
function setCurrentArea(user, id)
    user.currDungeonId = id;
end

------------------------------------------------
-- 获取当前地图区域
-- @function [parent=#DungeonAreaM] getCurrentArea
-- @param user 玩家对象
function getCurrentArea(user)
    return user.currDungeonId;
end

------------------------------------------------
-- 获取下一个主地图区域
-- @function [parent=#DungeonAreaM] getCurrentArea
-- @param dungeonId 区域id
function getNextMainArea(dungeonId)
    if not areas[dungeonId] then
        return nil;
    end

    for id, info in pairs(areas) do
        if info.pre_area_id ~= 0 and areas[info.pre_area_id] ~= nil then
            local parentId = areas[info.pre_area_id].parent_id;
            local targetParentId = getParentId(id);
            if parentId == dungeonId and targetParentId ~= parentId then
                return targetParentId;
            end
        end
    end

    return nil;
end

------------------------------------------------
-- 获取下一个地图区域
-- @function [parent=#DungeonAreaM] getCurrentArea
-- @param dungeonId 区域id
function getNextArea(dungeonId)
    if not areas[dungeonId] then
        return;
    end

    for id, info in pairs(areas) do
        if info.pre_area_id == dungeonId then
            return id;
        end
    end
end

------------------------------------------------
-- 获取下一个子地图区域
-- @function [parent=#DungeonAreaM] getCurrentArea
-- @param dungeonId 区域id
function getNextChildArea(dungeonId)
    if not areas[dungeonId] then
        return;
    end

    for id, info in pairs(areas) do
        if info.pre_area_id == dungeonId and info.type == "child" then
            return id;
        end
    end
end

------------------------------------------------
-- 获取主地图区域
-- @function [parent=#DungeonAreaM] getParentId
-- @param dungeonId 区域id
function getParentId(dungeonId)
    local info = query(dungeonId) or {};
    if info == nil or type(info) ~= "table" then
        return 0;
    end

    -- 如果是活动迷宫
    if info["type"] == "activity" then
        if info["parent_id"] == 0 then
            return dungeonId;
        end

        return info["parent_id"] or 0;
    end

    if info["type"] ~= "child" then
        -- 自己就是主地牢
        return dungeonId;
    end

    return info["parent_id"] or 0;
end

------------------------------------------------
-- 查询子区域是否已经解锁
-- @function [parent=#DungeonAreaM] isSubAreaUnlocked
-- @param user 玩家对象
-- @param id   地牢区域ID
function isSubAreaUnlocked(user, id)
    -- 没有前置区域限制
    local preAreaId = query(id, "pre_area_id");
    if preAreaId == 0 then
        return true;
    end

    -- 前置区域通关且奖励已领取，后续区域可解锁
    if isDungeonPassed(user, preAreaId) then
        return true;
    end

    -- 是否强制开启
    if isDungeonOpened(user, id) then
        return true;
    end

    return false;
end

------------------------------------------------
-- 查询是否在同一个大区域内
-- @function [parent=#DungeonAreaM] isInSameMainArea
-- @param id1  地牢区域ID1
-- @param id2  地牢区域ID2
function isInSameArea(id1, id2)
    local parent_id1 = query(id1, "parent_id");
    if parent_id1 == 0 then
        parent_id1 =  id1;
    end

    local parent_id2 = query(id2, "parent_id");
    if parent_id2 == 0 then
        parent_id2 =  id2;
    end

    return parent_id1 == parent_id2;
end

------------------------------------------------
-- 查询区域是否已经解锁
-- 这里的区域不是指parent_id对应的区域，而是指整个迷宫区域
-- @function [parent=#DungeonAreaM] isSubAreaUnlocked
-- @param user 玩家对象
-- @param id   地牢区域ID
function isAreaUnlocked(user, id)
    -- 有解锁的子区域，则本区域解锁
    local list = subAreaList[id];
    if list == nil then
        return false;
    end

    for _, subId in pairs(list) do
        if isSubAreaUnlocked(user, subId) then
            return true;
        end
    end

    return false;
end

------------------------------------------------
-- 查询区域是否已经解锁
-- @function [parent=#DungeonAreaM] isSubAreaUnlocked
-- @param user 玩家对象
-- @param id   地牢区域ID
function isParentAreaUnlocked(user, id)
    -- 有解锁的子区域，则本区域解锁
    local list = subAreaList[id];
    if list == nil then
        return false;
    end

    for _, subId in pairs(list) do
        if not isSubAreaUnlocked(user, subId) then
            return false;
        end
    end

    return true;
end

------------------------------------------------
-- 强制解锁某个区域
-- @function [parent=#DungeonAreaM] forceUnlockArea
-- @param user  玩家对象
-- @param id    区域id
-- @return 是否解锁成功
function forceUnlockArea(user, id)
    -- 该区域已经解锁
    if isAreaUnlocked(user, id) then
        return false;
    end

    trace("DungeonAreaM", "强制解锁区域%d", id);

    local maps = user.dbase:queryEx(OPEN_DUNGEON_PATH, {});
    maps[id] = 1;

    user.dbase:setEx(OPEN_DUNGEON_PATH, maps);

    -- 抛出强制解锁事件
    EventMgr.fire(event.UNLOCK_DUNGEON_AREA, { ["id"] = id, });
end

------------------------------------------------
-- 标记某个区域已通关
-- @function [parent=#DungeonAreaM] setDungeonPassed
-- @param user  玩家对象
-- @param id    区域id
function setDungeonPassed(user, id)
    local maps = user.dbase:queryEx(PASS_DUNGEON_PATH, {});
    if type(maps[id]) == "number" and maps[id] >= 1 then
        return;
    end

    maps[id] = 1;

    user.dbase:setEx(PASS_DUNGEON_PATH, maps);
end

------------------------------------------------
-- 某个区域是否已通关
-- @function [parent=#DungeonAreaM] isDungeonPassed
-- @param user  玩家对象
-- @param id    区域id
function isDungeonPassed(user, id)
    local maps = user.dbase:queryEx(PASS_DUNGEON_PATH, {});
    if type(maps[id]) == "number" and maps[id] > 0 then
        return true;
    end
    return false;
end

-- 是否有子区域通过过
function isSubDungeonPassed(user, parentId)
    local subAreaList = getSubAreaList(parentId);

    for i = 1, #subAreaList do
        local areaId = subAreaList[i];
        if query(areaId, "parent_id") ~= 0 then
            if isDungeonPassed(user, areaId) then
                return true;
            end
        end
    end
    return false;
end

------------------------------------------------
-- 某个区域是否强制开启
-- @function [parent=#DungeonAreaM] isDungeonPassed
-- @param user  玩家对象
-- @param id    区域id
function isDungeonOpened(user, id)
    local maps = user.dbase:queryEx(OPEN_DUNGEON_PATH, {});
    if type(maps[id]) == "number" and maps[id] > 0 then
        return true;
    end
end

------------------------------------------------
-- 某个区域的子区域是否全部通关
-- @function [parent=#DungeonAreaM] isAllSubAreaPassed
-- @param user  玩家对象
-- @param id    区域id
function isAllSubAreaPassed(user, id)
    local subAreaList = getSubAreaList(id);

    local allSubAreaPassed = true;
    for i = 1, #subAreaList do
        local areaId = subAreaList[i];
        if query(areaId, "parent_id") ~= 0 then
            if not isDungeonPassed(user, areaId) then
                allSubAreaPassed = false;
            end
        end
    end

    return allSubAreaPassed;
end

------------------------------------------------
-- 某个区域的子区域是否全部通关并且奖励已领取
-- @function [parent=#DungeonAreaM] isAllSubAreaBonusTaken
-- @param user  玩家对象
-- @param id    区域id
function isAllSubAreaBonusTaken(user, id)
    local subAreaList = getSubAreaList(id);

    local allSubAreaBonusTaken = true;
    for i = 1, #subAreaList do
        local areaId = subAreaList[i];
        if query(areaId, "parent_id") ~= 0 then
            if not isDungeonPassed(user, areaId) or
                not isDungeonPassBonusTaken(user, areaId) then
                allSubAreaBonusTaken = false;
            end
        end
    end

    return allSubAreaBonusTaken;
end

-- 是否是无尽模式的迷宫
function isEndlessArea(id)
    local areaType = query(id, "type")

    return areaType == "main" or areaType == "dlc";
end

------------------------------------------------
-- 标记某个区域通关奖励已领取
-- @function [parent=#DungeonAreaM] setDungeonPassBonusTaken
-- @param user  玩家对象
-- @param id    区域id
function setDungeonPassBonusTaken(user, id)
    local maps = user.dbase:queryEx(PASS_DUNGEON_PATH, {});
    maps[id] = 2;

    user.dbase:setEx(PASS_DUNGEON_PATH, maps);
end

------------------------------------------------
-- 某个区域通关奖励是否已领取
-- @function [parent=#DungeonAreaM] isDungeonPassBonusTaken
-- @param user  玩家对象
-- @param id    区域id
function isDungeonPassBonusTaken(user, id)
    local maps = user.dbase:queryEx(PASS_DUNGEON_PATH, {});
    if type(maps[id]) == "number" and maps[id] == 2 then
        return true;
    end
end

---------------------------
-- 获取某区域的冈布奥
-- @param id  区域id
function getAreaSlime(id)
    local slime = query(id, "slime");
    local values = table.keys(slime)
    if type(slime) ~= 'table' or #table.keys(values) == 0 then
        return nil;
    end

    return slime;
end

-----------------------------
-- 获取前置区域是preAreaId的区域
-- @param preAreaId  前置区域id
function getAreaByPreArea(preAreaId)
    local ids = {};
    for _, info in pairs(table.values(areas)) do
        if info["pre_area_id"] == preAreaId then
            table.insert(ids, info["id"]);
        end
    end

    if #ids > 0 then
        table.sort(ids);
        return ids;
    end

    return nil;
end

-- 获取强化列表
function getStrengthenList()
    return workshopStrengthen;
end

-- 获取熔炉强化等级
function getStrengthenLevel(id)
    for tempId, info in pairs(workshopStrengthen) do
        if tonumber(tempId) == id then
            return info.level or 1;
        end
    end

    return 1;
end

-----------------------------
-- 获取能产出材料的最大阶位
function getMaxRankMaterial()
    local ret = getOpenResource();

    local maxRank = 0;
    for _, classId in pairs(ret) do
        local rank = ItemM.query(classId, "rank");
--        rank = FormulaM.invoke("GET_MAX_UPGRADE_RANK", rank);
        if rank > maxRank then
            maxRank = rank;
        end
    end

    return maxRank;
end

-----------------------------
-- 获取当前激活的炼金资源
function getOpenResource()
    local ret = {};
    local workshopStrengthen = AlchemyWorkshopM.getStrengthenList();
    for tempId, info in pairs(workshopStrengthen) do
        if info.level > 0 then
            local resId = AlchemyWorkshopM.query(info.id, "open_resource");
            if resId ~= nil and resId > 0 then
                ret = table.append(ret, { resId });
            end
        end
    end
    return ret;
end

-----------------------------
-- 领取区域通关奖励
function takeBonus(user, id)
    if not isDungeonPassed(user, id) then
        return false;
    end

    if isDungeonPassBonusTaken(user, id) then
        return false;
    end

    setDungeonPassBonusTaken(user, id);
    local bonus = doBonus(user, id);
    return true, bonus;
end

function calcPassBonus(user, id)
    local areaInfo = query(id);
    if areaInfo == nil then
        return;
    end

    local formulaName = areaInfo["pass_bonus_formula"];
    return FormulaM.invoke(formulaName, ME.user, id, areaInfo["pass_bonus_args"]);
end

-----------------------------
-- 执行通关奖励
function doBonus(user, id)
    -- 计算通关奖励
    local bonusList = calcPassBonus(user, id);
    if bonusList == nil then
        return;
    end
    -- 奖励为life时，会改变bonusList的值，所以定义个新的
    local List = calcPassBonus(user, id);
    for _, bonus in pairs(bonusList) do
        BonusM.doBonus(bonus, "dungeon_pass_bonus");
    end

    return List;
end

-------------------------------
-- 获取已解锁未激活的炼金资源
function getUnlockResource()
    local ret = {};
    local workshopStrengthen = AlchemyWorkshopM.getStrengthenList();
    for tempId, info in pairs(workshopStrengthen) do
        if info.level == 0 then
            local resId = AlchemyWorkshopM.query(info.id, "open_resource");
            if resId ~= nil and resId > 0 then
                ret = table.append(ret, { resId });
            end
        end
    end
    return ret;
end

-------------------------------
-- 能否领取通关活力奖励
function canTakeDungeonPassBonus(id)
    local maxLife = ME.user:getMaxLife();
    local life    = ME.user.dbase:query("life", 0);

    -- 有活力奖励且活力达到上限时
    if isLifeBonusArea(id) and life >= maxLife then
        return "life_full";
    end

    return true;
end

-------------------------------
-- 是否有活力奖励
function isLifeBonusArea(id)
    local bonusList = calcPassBonus(ME.user, id);

    -- 有活力奖励的
    if bonusList ~= nil then
        for _, bonus in pairs(bonusList) do
            if bonus[1] == 2 and bonus[2] == "life" and bonus[3] > 0 then
                return true;
            end
        end
    end

    return false;
end

-- 判断dp值是否足够解锁
function isDpEnoughToUnlock(id)
    -- 获取解锁需求dp
    local requireDp = query(id, "unlock_dp") or 0;
    if type(requireDp) ~= "number" or requireDp <= 0 then
        return true;
    end

    -- 计算当前已累积的dp总值
    local curDp = AchievementM.calcAreasCumulateDp();
    return curDp >= requireDp;
end

-- 是否领取了解锁奖励
function isUnlockBonusTaken(id)
    local bonus = query(id, "unlock_bonus");
    if type(bonus) ~= "table" or #bonus == 0 then
        -- 没有配置奖励，默认已领走
        return true;
    end

    local bonusRecord = ME.user.dbase:queryEx(UNLOCK_BONUS_PATH, {});
    if #table.keys(bonusRecord) == 0 then
        return false;
    end

    return bonusRecord[id] == 1;
end

-- 领取区域解锁奖励
function takeUnlockBonus(id)
    if not isAreaUnlocked(ME.user, id) then
        -- 区域尚未解锁，无法领取奖励
        trace("DungeonAreaM", "关卡(%d)尚未解锁，无法领取解锁奖励！", id);
        return false;
    end

    if isUnlockBonusTaken(id) then
        -- 已经领取过奖励了
        trace("DungeonAreaM", "已经领取过关卡(%d)的解锁奖励！", id);
        return false;
    end

    if not isDpEnoughToUnlock(id) then
        -- dp值不足
        trace("DungeonAreaM", "dp值不足，无法领取解锁奖励", id);
        return false;
    end

    local bonusList = query(id, "unlock_bonus");
    if type(bonusList) ~= 'table' or #bonusList == 0 then
        -- 没有配置奖励
        trace("DungeonAreaM", "没有配置关卡(%d)的解锁奖励", id);
        return false;
    end

    -- 标记已经领取奖励
    local bonusRecord = ME.user.dbase:queryEx(UNLOCK_BONUS_PATH, {});
    bonusRecord[id] = 1;
    ME.user.dbase:setEx(UNLOCK_BONUS_PATH, bonusRecord);

    -- 执行奖励
    for _, bonus in pairs(bonusList) do
        BonusM.doBonus(bonus, "area_unlock_bonus");
    end

    -- 抛出事件
    EventMgr.fire(event.TAKE_UNLOCK_BONUS, { ["bonusList"] = bonusList, ["areaId"] = id, });

    return true;
end

-- 查询当前关闭的地牢
function getClosedArea()
    return closedArea;
end

-- 设置当前关闭的地牢
function setClosedArea(list)
    closedArea = list;
end

-- 查看当前副本是否开放
function isOpenedArea(dungeonId)
    -- 获取主地牢id
    dungeonId = getParentId(dungeonId);

    local closeDungeonArea = closedArea;
    if sizeof(closeDungeonArea) <= 0 then
        return true;
    end

    if table.indexOf(closeDungeonArea, dungeonId) <= 0 then
        return true;
    end

    return false;
end

-- 计算地牢体力消耗
function calcDungeonLifeCost(dungeonId)
    local cost = query(dungeonId, "require_life");

    if type(cost) == "table" then
        if isDungeonPassed(ME.user, dungeonId) then
            return cost["passed"];
        else
            return cost["new"];
        end
    end

    return cost;
end